package com.kexio.user.controller;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.kexio.auth.annotation.RateLimit;
import com.kexio.auth.annotation.RequiresPermission;
import com.kexio.auth.dto.LoginRequest;
import com.kexio.auth.dto.LoginResponse;
import com.kexio.auth.util.AuthContextUtils;
import com.kexio.auth.util.IpUtils;
import com.kexio.common.dto.Result;
import com.kexio.common.dto.SessionInfo;
import com.kexio.user.dto.LogoutRequest;
import com.kexio.user.dto.OnlineUserDetailDTO;
import com.kexio.user.dto.RefreshTokenRequest;
import com.kexio.user.dto.UserDTO;
import com.kexio.user.service.AuthService;
import com.kexio.user.service.EmailVerificationService;
import com.kexio.user.service.UserService;

import cn.dev33.satoken.annotation.SaIgnore;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;

/**
 * 认证控制器
 * 
 * 提供用户认证相关的REST API，包括：
 * - 用户登录
 * - 令牌刷新
 * - 用户登出
 * - 用户信息获取
 * - 会话管理
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@RestController
@RequestMapping("/auth")
@Tag(name = "认证管理", description = "用户认证相关API")
public class AuthController {
    
    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
    
    @Autowired
    private AuthService authService;
    
    @Autowired
    private UserService userService;
    
    @Autowired(required = false)
    private EmailVerificationService emailVerificationService;
    
    // ==================== 认证相关API ====================
    
    /**
     * 用户注册
     */
    @PostMapping("/register")
    @SaIgnore
    @RateLimit(count = 5, time = 1)
    @Operation(summary = "用户注册", description = "创建新用户账号，注册成功后需要手动登录")
    public Result<Void> register(
            @org.springframework.validation.annotation.Validated(UserDTO.CreateGroup.class) 
            @RequestBody UserDTO userDTO,
            HttpServletRequest request) {
        
        logger.info("用户注册请求: username={}, email={}, tenantId={}", 
            userDTO.getUsername(), userDTO.getEmail(), userDTO.getTenantId());
        
        // 执行注册（不自动登录）
        authService.register(userDTO, request);
        
        logger.info("用户注册成功: username={}", userDTO.getUsername());
        
        return Result.success("注册成功，请使用您的账号和密码登录");
    }
    
    /**
     * 发送邮箱验证码
     */
    @PostMapping("/send-email-code")
    @SaIgnore
    @RateLimit(count = 3, time = 1)
    @Operation(summary = "发送邮箱验证码", description = "向指定邮箱发送验证码")
    public Result<Void> sendEmailCode(@RequestParam("email") String email) {
        
        logger.info("发送邮箱验证码请求: email={}", email);
        
        boolean success = emailVerificationService.sendVerificationCode(email);
        
        if (success) {
            logger.info("验证码发送成功: email={}", email);
            return Result.success("验证码已发送，请查收邮箱（5分钟内有效）");
        } else {
            logger.warn("验证码发送失败: email={}", email);
            return Result.error("验证码发送失败，请稍后再试");
        }
    }
    
    /**
     * 用户登录
     */
    @PostMapping("/login")
    @SaIgnore
    @RateLimit(count = 10, time = 1)
    @Operation(summary = "用户登录", description = "验证用户凭据并返回访问令牌")
    public Result<LoginResponse> login(
            @Valid @RequestBody LoginRequest loginRequest,
            HttpServletRequest request) {
        
        logger.info("用户登录请求: username={}, tenantId={}, loginType={}", 
            loginRequest.getUsername(), loginRequest.getTenantId(), loginRequest.getLoginType());
        
        // 设置客户端信息
        loginRequest.setClientIp(IpUtils.getClientIp(request));
        loginRequest.setUserAgent(request.getHeader("User-Agent"));
        
        // 执行登录
        LoginResponse loginResponse = authService.login(loginRequest, request);
        
        logger.info("用户登录成功: username={}, userId={}, returnToken={}", 
            loginRequest.getUsername(), 
            loginResponse.getUserInfo() != null ? loginResponse.getUserInfo().getUserId() : "unknown",
            loginResponse.getAccessToken() != null ? 
                loginResponse.getAccessToken().substring(0, Math.min(loginResponse.getAccessToken().length(), 8)) + "..." : "null");
        
        return Result.success("登录成功", loginResponse);
    }
    
    /**
     * 刷新访问令牌
     */
    @PostMapping("/refresh")
    @SaIgnore
    @RateLimit(count = 20, time = 1)
    @Operation(summary = "刷新令牌", description = "使用刷新令牌获取新的访问令牌")
    public Result<LoginResponse> refreshToken(
            @Valid @RequestBody RefreshTokenRequest refreshRequest,
            HttpServletRequest request) {
        
        logger.debug("刷新令牌请求: tenantId={}", refreshRequest.getTenantId());
        
        // 设置客户端信息
        refreshRequest.setClientIp(IpUtils.getClientIp(request));
        refreshRequest.setUserAgent(request.getHeader("User-Agent"));
        
        // 执行令牌刷新
        LoginResponse loginResponse = authService.refreshToken(refreshRequest, request);
        
        logger.debug("令牌刷新成功: tenantId={}", refreshRequest.getTenantId());
        
        return Result.success("令牌刷新成功", loginResponse);
    }
    
    /**
     * 用户登出
     */
    @PostMapping("/logout")
    @RateLimit(count = 30, time = 1)
    @Operation(summary = "用户登出", description = "注销当前用户会话")
    public Result<String> logout(
            @RequestBody(required = false) LogoutRequest logoutRequest,
            HttpServletRequest request) {
        
        logger.debug("用户登出请求");
        
        // 如果请求体为空，创建默认的登出请求
        if (logoutRequest == null) {
            logoutRequest = new LogoutRequest("manual");
        }
        
        // 设置客户端信息
        logoutRequest.setClientIp(IpUtils.getClientIp(request));
        logoutRequest.setUserAgent(request.getHeader("User-Agent"));
        
        // 从Header中获取访问令牌（如果请求体中没有）
        if (logoutRequest.getAccessToken() == null) {
            String authHeader = request.getHeader("Authorization");
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                logoutRequest.setAccessToken(authHeader.substring(7));
            }
        }
        
        // 执行登出
        boolean success = authService.logout(logoutRequest, request);
        
        if (success) {
            logger.info("用户登出成功");
            return Result.success("登出成功");
        } else {
            logger.warn("用户登出处理异常");
            return Result.success("登出处理完成");
        }
    }
    
    /**
     * 获取当前用户信息
     */
    @GetMapping("/me")
    @Operation(summary = "获取当前用户信息", description = "返回当前登录用户的详细信息")
    public Result<UserDTO> getCurrentUser() {
        
        logger.debug("获取当前用户信息请求");
        
        UserDTO userProfile = authService.getCurrentUserProfile();
        
        logger.debug("获取用户信息成功: userId={}, username={}", 
            userProfile.getUserId(), userProfile.getUsername());
        
        return Result.success("获取用户信息成功", userProfile);
    }

    /**
     * 获取当前用户的完整权限集（权限码列表）
     * 供前端按钮显隐与鉴权使用。
     */
    @GetMapping("/permissions/current")
    @Operation(summary = "获取当前用户权限集", description = "返回当前登录用户的所有权限码")
    public Result<List<String>> getCurrentUserPermissions() {
        UserDTO current = authService.getCurrentUserProfile();
        List<String> perms = userService.getUserPermissions(current.getUserId());
        return Result.success("获取权限成功", perms);
    }
    
    // ==================== 令牌验证API ====================
    
    /**
     * 验证访问令牌
     */
    @PostMapping("/validate")
    @SaIgnore
    @RateLimit(count = 60, time = 1)
    @Operation(summary = "验证令牌", description = "验证访问令牌是否有效")
    public Result<Boolean> validateToken(
            @RequestParam("token") String accessToken,
            @RequestParam(value = "tenantId", required = false) String tenantId) {
        
        logger.debug("验证令牌请求: tenantId={}", tenantId);
        
        boolean isValid = authService.validateAccessToken(accessToken, tenantId);
        
        logger.debug("令牌验证结果: valid={}", isValid);
        
        return Result.success("令牌验证完成", isValid);
    }
    
    // ==================== 管理员API ====================
    
    /**
     * 强制用户登出（管理员）
     */
    @PostMapping("/force-logout")
    @RequiresPermission("auth:user:force-logout")
    @RateLimit(count = 10, time = 1)
    @Operation(summary = "强制用户登出", description = "管理员强制指定用户登出")
    public Result<String> forceUserLogout(
            @RequestParam("userId") String userId,
            @RequestParam(value = "reason", required = false) String reason) {
        
        logger.info("强制用户登出请求: userId={}, reason={}", userId, reason);
        
        String operatorId = AuthContextUtils.getCurrentUserId();
        boolean success = authService.forceLogout(
            userId, operatorId,
            reason != null ? reason : "管理员强制登出");
        
        if (success) {
            logger.info("强制用户登出成功: userId={}, operatorId={}", userId, operatorId);
            return Result.success("用户登出成功");
        } else {
            logger.warn("强制用户登出失败: userId={}", userId);
            return Result.error("用户登出失败");
        }
    }
    
    /**
     * 获取租户活跃会话列表（管理员）
     */
    @GetMapping("/sessions/tenant")
    @RequiresPermission("auth:session:tenant-list")
    @Operation(summary = "租户活跃会话列表", description = "获取当前租户的所有活跃会话，需管理员权限")
    public Result<List<SessionInfo>> getTenantSessions(
            @RequestParam(value = "limit", required = false) Integer limit) {
        UserDTO currentUser = authService.getCurrentUserProfile();
        
        // 获取租户的所有活跃会话
        List<SessionInfo> sessions = authService.getTenantActiveSessions(currentUser.getTenantId());
        
        // 应用数量限制
        if (limit != null && limit > 0 && sessions.size() > limit) {
            sessions = sessions.subList(0, limit);
        }
        
        logger.debug("✅ 获取租户活跃会话成功: tenantId={}, sessionCount={}, limited={}", 
                   currentUser.getTenantId(), sessions.size(), limit);
        return Result.success("获取租户活跃会话成功", sessions);
    }

    /**
     * 获取全局活跃会话列表（超级管理员）
     */
    @GetMapping("/sessions/all")
    @RequiresPermission("auth:session:all-list")
    @Operation(summary = "全局活跃会话列表", description = "获取所有租户的活跃会话，需超级管理员权限")
    public Result<List<SessionInfo>> getAllSessions(
            @RequestParam(value = "limit", required = false) Integer limit) {
        // ✨ 现代架构: 全局会话查询 - 查询所有租户的活跃会话
        List<SessionInfo> sessions = authService.getAllActiveSessions();
        
        // 应用数量限制
        if (limit != null && limit > 0 && sessions.size() > limit) {
            sessions = sessions.subList(0, limit);
        }
        
        logger.debug("✅ 获取全局活跃会话成功: sessionCount={}, limited={}", sessions.size(), limit);
        return Result.success("获取全局活跃会话成功", sessions);
    }

    /**
     * 获取在线用户数量统计
     */
    @GetMapping("/online-count")
    @RequiresPermission("auth:stats:online")
    @Operation(summary = "在线用户统计", description = "获取租户内在线用户数量")
    public Result<Long> getOnlineUserCount() {
        
        logger.debug("获取在线用户数量请求");
        
        // 使用认证上下文中的租户ID
        String currentTenantId = AuthContextUtils.getCurrentTenantId();
        long onlineCount = authService.getOnlineUserCount(currentTenantId);
        
        logger.debug("获取在线用户数量成功: tenantId={}, count={}", currentTenantId, onlineCount);
        
        return Result.success("获取在线用户数量成功", onlineCount);
    }
    
    /**
     * 获取在线用户详细信息
     */
    @GetMapping("/online-details")
    @RequiresPermission("auth:stats:online")
    @Operation(summary = "在线用户详细信息", description = "获取在线用户的详细统计信息，包括用户列表、设备分布等")
    public Result<OnlineUserDetailDTO> getOnlineUserDetails(
            @Parameter(description = "是否包含详细用户列表")
            @RequestParam(value = "includeDetails", defaultValue = "true") Boolean includeDetails) {
        
        logger.debug("获取在线用户详细信息请求: includeDetails={}", includeDetails);
        
        String currentTenantId = AuthContextUtils.getCurrentTenantId();
        OnlineUserDetailDTO details = authService.getOnlineUserDetails(currentTenantId, includeDetails);
        
        logger.debug("获取在线用户详细信息成功: tenantId={}, 总数={}", 
                    currentTenantId, details.getStatistics().getTotalCount());
        
        return Result.success("获取在线用户详细信息成功", details);
    }
    
    /**
     * 封禁用户（管理员）
     */
    @PostMapping("/user/ban")
    @RequiresPermission("auth:user:ban")
    @RateLimit(count = 10, time = 1)
    @Operation(summary = "封禁用户", description = "使用Sa-Token原生API封禁指定用户")
    public Result<String> banUser(
            @RequestParam("userId") String userId,
            @RequestParam(value = "reason", required = false, defaultValue = "违反使用规定") String reason,
            @RequestParam(value = "banDays", required = false, defaultValue = "-1") Integer banDays) {
        
        logger.info("封禁用户请求: userId={}, reason={}, banDays={}", userId, reason, banDays);
        
        String currentUserId = AuthContextUtils.getCurrentUserId();
        
        if (userId.equals(currentUserId)) {
            return Result.error("不能封禁自己");
        }
        
        boolean banned = userService.banUser(userId, reason, banDays);
        if (!banned) {
            return Result.error("封禁用户失败");
        }
        
        logger.info("封禁用户成功: userId={}, operator={}, banDays={}", userId, currentUserId, banDays);
        
        String message = banDays == -1 ? "用户已永久封禁并强制下线" : "用户已封禁" + banDays + "天并强制下线";
        return Result.success(message);
    }
    
    /**
     * 解封用户（管理员）
     */
    @PostMapping("/user/unban")
    @RequiresPermission("auth:user:ban")
    @RateLimit(count = 10, time = 1)
    @Operation(summary = "解封用户", description = "使用Sa-Token原生API解除用户封禁")
    public Result<String> unbanUser(@RequestParam("userId") String userId) {
        
        logger.info("解封用户请求: userId={}, operator={}", userId, AuthContextUtils.getCurrentUserId());
        
        boolean unbanned = userService.unbanUser(userId);
        if (!unbanned) {
            return Result.error("解封用户失败");
        }
        
        logger.info("解封用户成功: userId={}", userId);
        return Result.success("用户已解封");
    }
    
    /**
     * 刷新用户权限（管理员）
     */
    @PostMapping("/user/refresh-permissions")
    @RequiresPermission("auth:user:refresh-permissions")
    @RateLimit(count = 20, time = 1)
    @Operation(summary = "刷新用户权限", description = "刷新在线用户的权限缓存，使权限变更立即生效")
    public Result<String> refreshUserPermissions(@RequestParam("userId") String userId) {
        
        logger.info("刷新用户权限请求: userId={}, operator={}", userId, AuthContextUtils.getCurrentUserId());
        
        boolean refreshed = authService.refreshUserPermissions(userId);
        if (!refreshed) {
            logger.warn("刷新权限失败: userId={}, 用户可能不在线", userId);
            return Result.error("刷新权限失败，用户可能不在线");
        }
        
        logger.info("刷新权限成功: userId={}", userId);
        return Result.success("权限刷新成功，用户下次请求将使用新权限");
    }
    
    // ==================== 工具方法 ====================
    
    // ⚠️ 已删除不安全的公开密码重置接口
    // 原因：任何人无需认证即可重置admin密码，存在严重安全风险
    // 替代方案：
    //   1. 使用数据库初始化脚本设置初始密码
    //   2. 通过超级管理员界面重置密码（需要SUPER_ADMIN权限）
    //   3. 使用命令行工具或环境变量初始化
    
    /**
     * 获取客户端真实IP地址
     */
    // IP 解析统一由 IpUtils 提供
}
